#include <iostream>
#include <mutex>
#include <condition_variable>
#include <queue>
#include <atomic>

using namespace std;

namespace wzx
{
    //这里我实现的线程池是一个单productor(主线程)多consumer(从线程)模型
    template<typename T>
    class block_queue
    {
    public:
        block_queue(bool running_tag, int capacity = 5)
        :_capacity(capacity)
        ,_running_tag(running_tag)//thread_pool必须显示传
        {}

        void tag_run()
        {
            _running_tag = true;
        }

        void tag_stop()
        {
            _running_tag = false;
        }
        
        bool full()
        {
            return _bq.size() >= _capacity;
        }

        bool empty()
        {
            unique_lock<mutex> lmtx(_mtx);
            return _bq.empty();
        }


        //productor调用
        void push(const T& task)//线程池这里只有一个生产者, 所以不存在重入push的情景, 即没有多生产者间互斥问题
        {
            unique_lock<mutex> lmtx(_mtx);

            //if(full())
                // 缺陷1: pthread_cond_wait()是函数, 有可能调用失败, 
                //        以至于该线程未在pthread_cond_wait()下等待, 直接执行后续代码, 但此时队列是满的, 生产超额了
                // 缺陷2：假设生产端有多个, 且此时队列只有一个空位待生产, 且消费端是用broadcast唤醒所有生产端, 
                //        第一个竞争到锁的生产端线程已经把空位填满了, 而剩下的线程在竞争到锁后就会超额生产 -- 伪唤醒
            while(full())//检测生产条件是否满足
            {
                _productor_cond.wait(lmtx);
            }
            //用while循环判断, 出判断后队列一定未满, 可以生产
            
            _bq.push(task);

            //此时队列一定不为空, 通知消费端消费
            _consumer_cond.notify_one();
        }

        //consumer调用
        //bool get_and_pop(T& t)
        T get_and_pop(atomic<bool>& flag)//输出型参数: 来表示任务是否获得成功
        {
            unique_lock<mutex> lmtx(_mtx);

            //if -- 问题同上
            // while(empty())//因为你打算暴露接口, 把该类做成一个线程安全的类, 所以empty也用了锁, 这里再用empty就会死锁
            // while(_bq.empty())
            while(_bq.empty() && _running_tag == true)
            {
                // ------------------- 
                _consumer_cond.wait(lmtx);//当线程池stop(tag为false)时, 会唤醒cond下的所有线程, 之后进入下面的return T()
            }
            //用while循环判断, 出判断后队列一定没空, 可以消费

            if(_bq.empty() && _running_tag == false)
            {
                flag = false;
                return T();//随便返回一个不可用对象
            }

            T ret = _bq.front();
            _bq.pop();

            //此时队列一定不为满, 通知生产端生产
            _productor_cond.notify_one();//没有线程在cond下等待时(线程池stop)什么都不会做

            flag = true;
            return ret;
        }

        void consumer_cond_notify_all()
        {
            _consumer_cond.notify_all();
        }

    private:
        int _capacity;
        std::queue<T> _bq;

        mutex _mtx;
        condition_variable _productor_cond;
        condition_variable _consumer_cond;

        std::atomic<bool> _running_tag; //为了解决线程池的join_all问题
    };
}

